package com.njym.payment.alipay.service;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.request.AlipayDataDataserviceBillDownloadurlQueryRequest;
import com.alipay.api.response.AlipayDataDataserviceBillDownloadurlQueryResponse;
import com.alipay.demo.trade.model.builder.AlipayTradeRefundRequestBuilder;
import com.alipay.demo.trade.model.result.AlipayF2FRefundResult;
import com.alipay.demo.trade.service.AlipayTradeService;
import com.alipay.demo.trade.service.impl.AlipayTradeServiceImpl;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.njym.config.SystemConfig;
import com.njym.entity.PlatformBusinessRec;
import com.njym.entity.SysPaymentMode;
import com.njym.payment.alipay.AliPayProperties;
import com.njym.payment.alipay.GetAliPayClient;
import com.njym.service.PlatformBusinessRecService;
import com.njym.tradeStatusConfig.AliTradeStatus;
import com.njym.util.FileUtils;
import com.njym.vo.ThirdPartyStatement;
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @program: AliPayService
 * @description:
 * @author: yh
 * @create: 2018-09-05 14:02
 **/
@Service
public class AliPayService {
    private static final org.slf4j.Logger log = LoggerFactory.getLogger(AliPayService.class);

    private AlipayTradeService alipayTradeService;

    @Autowired
    private SystemConfig systemConfig;

    @Autowired
    private PlatformBusinessRecService platformBusinessRecService;

    @Resource
    private GetAliPayClient getAliPayClient;

    /**
     * method: aliPayBill
     *
     * @Description:下载支付宝对账单
     * @param: [orgId, billDate]
     * 机构id
     * 账单日期 yyyy-MM-dd
     * @return: com.alibaba.fastjson.JSONObject
     * @author: yh
     * @date: 2018/9/12 9:23
     */
    public JSONObject aliPayBill(String orgId, String billDate) {
        JSONObject json = new JSONObject();
        AliPayProperties aliPayProperties = getAliPayClient.getAliPayPropertiesByOrgId(orgId);
        AlipayClient alipayClient = getAliPayClient.getClientByOrgId(orgId);
        AlipayDataDataserviceBillDownloadurlQueryRequest request = new AlipayDataDataserviceBillDownloadurlQueryRequest();
        if (StringUtils.isNotBlank(aliPayProperties.getAppAuthToken())) {
            request.putOtherTextParam("app_auth_token", aliPayProperties.getAppAuthToken());
        }
        request.setBizContent("{" + "\"bill_type\":\"trade\"," + "\"bill_date\":\"" + billDate + "\"" + "  }");
        String path = null;
        try {
            AlipayDataDataserviceBillDownloadurlQueryResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                //调用成功
                String[] dateArr = billDate.split("-");
                String filePathPrefix = systemConfig.getBillFilePath() + "/" + orgId + "/alipay/" + dateArr[0] + "/" + dateArr[1] + "/" + dateArr[2] + "/";
                //调用下载文件方法
                this.downBill(response.getBillDownloadUrl(), filePathPrefix, billDate);
                String zipPath = filePathPrefix + billDate + ".zip";
                File zipFile = new File(zipPath);
                path = FileUtils.unZipFiles(zipFile, filePathPrefix);
            } else {
                log.info("获取账单失败");
                json.put("result", "FAIL");
                return json;
            }
        }
        catch (AlipayApiException e) {
            log.info("{}, body: {}", e.getErrMsg());
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        json.put("result", "SUCCESS");
        if (path != null && path.contains("(汇总)")) {
            path = path.replace("(汇总)", "");
        }
        json.put("path", path);
        return json;
    }

    /**
     * 下载账单文件：
     *
     * @param
     * @return response
     */
    private String downBill(String billDownloadUrl, String filePathPrefix, String fileName) {
        //指定希望保存的文件路径
        String filePath = filePathPrefix + fileName + ".zip";
        URL url;
        HttpURLConnection httpUrlConnection = null;
        InputStream fis = null;
        FileOutputStream fos = null;
        try {
            url = new URL(billDownloadUrl);
            httpUrlConnection = (HttpURLConnection)url.openConnection();
            httpUrlConnection.setConnectTimeout(5 * 1000);
            httpUrlConnection.setDoInput(true);
            httpUrlConnection.setDoOutput(true);
            httpUrlConnection.setUseCaches(false);
            httpUrlConnection.setRequestMethod("GET");
            httpUrlConnection.setRequestProperty("Charsert", "UTF-8");
            httpUrlConnection.connect();
            fis = httpUrlConnection.getInputStream();
            byte[] temp = new byte[1024];
            int b;
            FileUtils.createFile(filePath);
            fos = new FileOutputStream(new File(filePath));
            while ((b = fis.read(temp)) != -1) {
                fos.write(temp, 0, b);
                fos.flush();
            }
        }
        catch (MalformedURLException e) {
            e.printStackTrace();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            try {
                if (fis != null) {
                    fis.close();
                }
                if (fos != null) {
                    fos.close();
                }
                if (httpUrlConnection != null) {
                    httpUrlConnection.disconnect();
                }
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 对账
     * @param orgId
     * @param modeId
     * @param path
     * @param billDate
     */
    public void reconciliationAliPayBill(String orgId, String modeId, String path, String billDate) {
        //读取path路径下的csv文件
        ArrayList<ArrayList<String>> arrayList = FileUtils.CSV2Array(path);
        List<ThirdPartyStatement> thirdPartyStatementList = new ArrayList<>();
        if (arrayList != null && arrayList.size() > 0) {
            for (int i = 5; i < arrayList.size(); i++) {
                ArrayList<String> stringArray = arrayList.get(i);
                //门店编号一直才能对账
                if (stringArray.size() > 1 && orgId.equals(stringArray.get(6).trim())) {
                    String tradeNo = stringArray.get(0).replace("\t", "");
                    String outTradeNo = stringArray.get(1).replace("\t", "");
                    // 业务类型
                    String recType = stringArray.get(2).trim();
                    // 完成时间
                    String time = stringArray.get(5);
                    SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    Date date = null;
                    try {
                        date = fmt.parse(time);
                    }catch (ParseException e){
                        log.info("时间转化异常:"+time, e);
                        e.printStackTrace();
                    }
	                // 费用
                    String totalFee = stringArray.get(12);
                    // 退款号
                    String refundNo = stringArray.get(21);
                    BigDecimal totalFeeBD = new BigDecimal(totalFee).setScale(2, BigDecimal.ROUND_HALF_UP);
                    ThirdPartyStatement thirdPartyStatement = new ThirdPartyStatement(tradeNo, outTradeNo, totalFeeBD,recType,date,refundNo);
                    thirdPartyStatementList.add(thirdPartyStatement);
                } else {
                    continue;
                }
            }
        }
        Wrapper<PlatformBusinessRec> queryParams = new EntityWrapper<>();
        queryParams.isNotNull("trade_no");
        queryParams.eq("org_id", orgId);
        queryParams.eq("mode_id", modeId);
        queryParams.between("create_date", billDate + " 00:00:00", billDate + " 23:59:59");
        List<PlatformBusinessRec> platformBusinessRecList = platformBusinessRecService.selectList(queryParams);

        if (thirdPartyStatementList.size() > 0) {
            //将支付宝对账单和平台交易记录对比
            try {
                for (ThirdPartyStatement thirdPartyStatement : thirdPartyStatementList) {
                    String thirdOutTradeNo = thirdPartyStatement.getOutTradeNo();
                    String thirdRefundNo = thirdPartyStatement.getRefundNo().replace("/t","").trim();
                    String recType = thirdPartyStatement.getRecType();
                    Byte recTypeByte;
	                if("交易".equals(recType)){
		                recTypeByte=Byte.valueOf("1");
	                }else if("退款".equals(recType)){
		                recTypeByte = Byte.valueOf("2");
	                }else {
		                //冲正
		                recTypeByte = Byte.valueOf("3");
	                }
                    boolean existFlag = false;
                    for (PlatformBusinessRec businessRec : platformBusinessRecList) {
                        if(businessRec.getRecType().equals(Byte.valueOf("1"))){
                            if (thirdOutTradeNo.equals(businessRec.getOutTradeNo()) && recTypeByte.equals(businessRec.getRecType())) {
                                existFlag = true;
                                break;
                            }
                        }else if (businessRec.getRecType().equals(Byte.valueOf("2"))){
                            if (thirdRefundNo.equals(businessRec.getOutRequestNo()) && recTypeByte.equals(businessRec.getRecType())) {
                                existFlag = true;
                                break;
                            }
                        }else {
                        }

                    }
                    if (!existFlag) {
                        //平台不存在支付记录，但是支付宝存在
                        //将支付宝中的这条记录存到平台数据库
                        PlatformBusinessRec platformBusinessRec = new PlatformBusinessRec();
                        platformBusinessRec.setRecType(recTypeByte);
                        platformBusinessRec.setOrgId(platformBusinessRecList.get(0).getOrgId());
                        platformBusinessRec.setChannelId(platformBusinessRecList.get(0).getChannelId());
                        platformBusinessRec.setModeId(platformBusinessRecList.get(0).getModeId());
                        platformBusinessRec.setTradeNo(thirdPartyStatement.getTradeNo());
                        platformBusinessRec.setOutTradeNo(thirdPartyStatement.getOutTradeNo());
                        platformBusinessRec.setOutRequestNo(thirdRefundNo);
                        BigDecimal fee = thirdPartyStatement.getTotalFee();
                        if(fee.compareTo(BigDecimal.ZERO)==-1){
                            //转为Double
                            Double dValue=-fee.doubleValue();
                             fee=BigDecimal.valueOf(dValue);
                        }
                        platformBusinessRec.setTotalFee(fee);
                        platformBusinessRec.setAccountFlag(Byte.valueOf("0"));
                        platformBusinessRec.setAbnormalFlag(Byte.valueOf("0"));
                        platformBusinessRec.setCreateDate(thirdPartyStatement.getTime());
                        platformBusinessRec.setTradeDate(thirdPartyStatement.getTime());
                        platformBusinessRec.setTradeStatus(AliTradeStatus.TRADE_SUCCESS);
                        platformBusinessRec.setDeleted(Byte.valueOf("0"));
                        platformBusinessRecService.insert(platformBusinessRec);
                    }
                }
                //支付宝不存在平台记录存在
                Wrapper<PlatformBusinessRec> queryParamss = new EntityWrapper<>();
                queryParamss.isNotNull("trade_no");
                queryParamss.eq("org_id", orgId);
                queryParamss.eq("mode_id", modeId);
                queryParamss.between("create_date", billDate + " 00:00:00", billDate + " 23:59:59");
                List<PlatformBusinessRec> platformBusinessRecLists = platformBusinessRecService.selectList(queryParams);

                Set<String> setList = thirdPartyStatementList.stream().map(ThirdPartyStatement::getOutTradeNo).collect(Collectors.toSet());
                for (PlatformBusinessRec businessRec : platformBusinessRecLists) {
                     if(!setList.contains(businessRec.getOutTradeNo())){
                         businessRec.deleteById();
                     }
                }

            }
            catch (Exception e) {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            }
        }
    }

    public String refund(String orgId, String outTradeNo, String refundAmount, String refundReason,String outRefundNo,String channelId,String operatorId,String operatorName) {
        AliPayProperties aliPayProperties = getAliPayClient.getAliPayPropertiesByOrgId(orgId);
        SysPaymentMode sysPaymentMode = getAliPayClient.getPaymentModeByOrgId(orgId);
        String flag="";
        alipayTradeService =
            new AlipayTradeServiceImpl.ClientBuilder().setGatewayUrl(aliPayProperties.getOpenApiDomain()).setAppid(aliPayProperties.getAppid()).setPrivateKey(aliPayProperties.getPrivateKey())
                .setAlipayPublicKey(aliPayProperties.getAlipayPublicKey()).setSignType(aliPayProperties.getSignType()).build();

        AlipayTradeRefundRequestBuilder builder =
            new AlipayTradeRefundRequestBuilder().setOutTradeNo(outTradeNo).setRefundAmount(refundAmount).setOutRequestNo(outRefundNo)
                .setRefundReason(refundReason).setAppAuthToken(aliPayProperties.getAppAuthToken());
        AlipayF2FRefundResult result = alipayTradeService.tradeRefund(builder);
        if (result.getResponse().getCode().equals(AliTradeStatus.QUERY_SUCCESS)) {
            switch (result.getTradeStatus()) {
                case SUCCESS:
                    if (result.getResponse().getFundChange().equals("Y")) {
                        // 将支付记录保存到数据库
                        PlatformBusinessRec businessRecRefund = new PlatformBusinessRec();
                        businessRecRefund.setOrgId(orgId);
                        businessRecRefund.setChannelId(channelId);
                        businessRecRefund.setModeId(sysPaymentMode.getId());
                        businessRecRefund.setRecType(Byte.valueOf("2"));
                        businessRecRefund.setTradeNo(result.getResponse().getTradeNo());
                        businessRecRefund.setOutTradeNo(outTradeNo);
                        businessRecRefund.setOutRequestNo(outRefundNo);
                        //设置小数位数，第一个变量是小数位数，第二个变量是取舍方法(四舍五入)
                        BigDecimal bd = new BigDecimal(result.getResponse().getRefundFee()).setScale(2, BigDecimal.ROUND_HALF_UP);
                        businessRecRefund.setTotalFee(bd);
                        businessRecRefund.setTradeDate(result.getResponse().getGmtRefundPay());
                        businessRecRefund.setTradeStatus(result.getTradeStatus().toString());
                        businessRecRefund.setAccountFlag(Byte.valueOf("0"));
                        businessRecRefund.setAbnormalFlag(Byte.valueOf("0"));
                        businessRecRefund.setCreateDate(new Date());
                        businessRecRefund.setOperatorId(operatorId);
                        businessRecRefund.setOperatorName(operatorName);
                        platformBusinessRecService.insert(businessRecRefund);
                        flag = "TRADE_SUCCESS";
                    } else if (result.getResponse().getFundChange().equals("N")) {
                        log.info("该订单已退款，请勿重复退款！");
                    }
                    break;
                case FAILED:
                    flag = "QUERY_FAILURE";
                    break;
                case UNKNOWN:
                    flag = "QUERY_FAILURE";
                    break;
                default:
                    flag = "QUERY_FAILURE";
            }
        } else {
            flag = "QUERY_FAILURE";
        }
        return flag;
    }
}
